La Arquitectura Hexagonal, propuesta por Alistair Cockburn, es un enfoque que busca separar la lógica de negocio de la infraestructura y las interfaces externas. También se conoce como "Ports and Adapters", ya que define una forma de estructurar las aplicaciones para que sean independientes de frameworks, bases de datos y sistemas externos.
Este modelo permite que la lógica de negocio sea reutilizable y fácil de probar, promoviendo un diseño limpio, desacoplado y flexible.
✅ Separación de Concerns (SoC - Separation of Concerns)
✅ Desacoplamiento
✅ Independencia de Infraestructura
✅ Testabilidad
✅ Extensibilidad
La arquitectura se organiza en tres capas:
Esta capa contiene las reglas de negocio y debe ser completamente independiente de la infraestructura.
📌 Componentes relacionados en Java:
✅ Avion → Entidad principal del dominio.
✅ AvionDomainService → Reglas de negocio encapsuladas en servicios.
✅ AvionRepository → Puerto de salida, define la interfaz de persistencia.
Define los flujos de negocio y casos de uso específicos sin preocuparse de los detalles de infraestructura.
📌 Componentes relacionados en Java:
✅ RegisterAvionUseCase → Caso de uso que maneja el registro de aviones.
✅ AvionDTO → Objeto que transporta datos sin lógica de negocio.
✅ AvionMapper → Convierte entre entidades de dominio y DTOs.
Contiene adaptadores para bases de datos, APIs externas, mensajería y otros sistemas externos.
📌 Componentes relacionados en Java:
✅ JpaAvionRepository → Adaptador de persistencia para JPA.
✅ EventPublisher → Adaptador para publicar eventos.
✅ ExternalFlightApiAdapter → Adaptador para integrar APIs externas.
✅ AvionController → Entrada HTTP en forma de REST API.
| 📂 Capa | 📜 Concepto | 🎯 Clase Java Relacionada | 🔍 Explicación |
|---|---|---|---|
| Dominio | Entidad | Avion | Representa un avión con su estado y comportamiento. |
| Dominio | Servicio de Dominio | AvionDomainService | Contiene reglas de negocio sin preocuparse por infraestructura. |
| Dominio | Puerto de salida | AvionRepository | Define la interfaz de persistencia sin depender de JPA o SQL. |
| Aplicación | Caso de Uso | RegisterAvionUseCase | Orquesta la lógica para registrar un avión. |
| Aplicación | DTO | AvionDTO | Transporta datos sin lógica de negocio. |
| Aplicación | Mapper | AvionMapper | Convierte entre entidades de dominio y DTOs. |
| Infraestructura | Adaptador de Persistencia | JpaAvionRepository | Implementa AvionRepository usando JPA. |
| Infraestructura | Adaptador de Mensajería | EventPublisher | Publica eventos en un sistema de mensajería. |
| Infraestructura | Adaptador de API Externa | ExternalFlightApiAdapter | Se comunica con APIs externas. |
| Infraestructura | Entrada REST | AvionController | Expone endpoints HTTP para interactuar con la aplicación. |
| Eventos | Evento de Dominio | AvionRegisteredEvent | Notifica que un avión ha sido registrado. |
| Eventos | Escucha de Evento | AvionEventListener | Reacciona a eventos publicados. |
La Arquitectura Hexagonal promueve una estructura modular y desacoplada, asegurando que los casos de uso y la lógica de negocio sean independientes de la infraestructura.
🚀 Beneficios en Microservicios:
✅ Facilita el reemplazo de tecnologías sin afectar el núcleo de la aplicación.
✅ Promueve la escalabilidad al permitir la integración con eventos y mensajería.
✅ Asegura alta cohesión y bajo acoplamiento, facilitando el mantenimiento.
Esta estructura garantiza un código más limpio, extensible y fácil de probar en entornos de microservicios y arquitectura basada en eventos.
💡 Descripción:
La lógica de negocio no debe depender directamente de la infraestructura (bases de datos, APIs externas, frameworks). En su lugar, debe depender de abstracciones (interfaces), permitiendo sustituir implementaciones sin afectar el núcleo de la aplicación.
📌 Ejemplo en el código:
🔹 AvionRepository (interfaz en el dominio) → Implementado por JpaAvionRepository (adaptador de infraestructura).
🔹 ¿Qué pasa si no se tiene en cuenta?
🚨 Si la lógica de negocio depende directamente de la infraestructura, cambiar la base de datos requeriría modificar toda la aplicación.
💡 Descripción:
Se pueden cambiar adaptadores (persistencia, mensajería, APIs) sin modificar el núcleo de la aplicación.
📌 Ejemplo en el código:
🔹 EventPublisher puede cambiar de Kafka a RabbitMQ sin afectar la lógica de eventos en AvionEventListener.
🔹 ¿Qué pasa si no se tiene en cuenta?
🚨 Se generaría un acoplamiento fuerte entre la lógica de negocio y una tecnología específica, dificultando cambios futuros.
💡 Descripción:
La aplicación no debe estar atada a un framework específico. Los frameworks deben ser herramientas y no la base del diseño.
📌 Ejemplo en el código:
🔹 AvionDomainService no usa anotaciones de Spring (@Service) porque pertenece al dominio puro.
🔹 ¿Qué pasa si no se tiene en cuenta?
🚨 Si se abusa de anotaciones/frameworks en la capa de dominio, se pierde flexibilidad y se dificulta la reutilización del código.
💡 Descripción:
En sistemas distribuidos o de microservicios, se recomienda el uso de eventos para minimizar el acoplamiento entre componentes.
📌 Ejemplo en el código:
🔹 AvionRegisteredEvent notifica a otros servicios cuando un avión ha sido registrado.
🔹 ¿Qué pasa si no se tiene en cuenta?
🚨 Sin eventos, los servicios tendrían que consultarse mutuamente, generando una carga excesiva en la red y reduciendo la escalabilidad.
💡 Descripción:
El código debe reflejar el lenguaje del negocio, usando entidades, servicios y eventos que tengan sentido en el dominio.
📌 Ejemplo en el código:
🔹 AvionDomainService encapsula reglas específicas como la capacidad del avión, en lugar de escribir reglas en los controladores.
🔹 ¿Qué pasa si no se tiene en cuenta?
🚨 La lógica de negocio se dispersa en múltiples capas, haciendo el sistema más difícil de mantener y entender.
💡 Descripción:
El diseño debe permitir probar el núcleo de negocio sin necesidad de infraestructura externa.
📌 Ejemplo en el código:
🔹 AvionDomainService puede probarse con pruebas unitarias sin necesidad de una base de datos o APIs externas.
🔹 ¿Qué pasa si no se tiene en cuenta?
🚨 Se requeriría levantar toda la infraestructura (base de datos, servicios externos) solo para probar la lógica de negocio, aumentando los tiempos y la complejidad de las pruebas.
💡 Descripción:
El sistema debe permitir agregar nuevas funcionalidades sin afectar el código existente.
📌 Ejemplo en el código:
🔹 Si en el futuro se requiere otro método de registro de aviones, simplemente se crea otro RegisterAvionUseCase sin modificar la implementación actual.
🔹 ¿Qué pasa si no se tiene en cuenta?
🚨 Se generarían modificaciones constantes en el código existente, aumentando el riesgo de errores.
| 📜 Postulado | Clase Java Relacionada | Ejemplo de Implementación |
|---|---|---|
| Inversión de Dependencias | AvionRepository, JpaAvionRepository | AvionRepository define el contrato, JpaAvionRepository lo implementa. |
| Intercambiabilidad de Adaptadores | EventPublisher, JpaAvionRepository, ExternalFlightApiAdapter | Se pueden cambiar adaptadores sin afectar el dominio. |
| Independencia del Framework | AvionDomainService, Avion | La lógica de negocio no usa anotaciones específicas de frameworks. |
| Comunicación Basada en Eventos | AvionRegisteredEvent, AvionEventListener | Se publican eventos para comunicar cambios en el sistema. |
| Expresividad del Dominio | AvionDomainService, AvionDTO | La lógica de negocio se expresa en términos del dominio. |
| Testabilidad desde el Núcleo | AvionDomainService, RegisterAvionUseCase | Se pueden probar sin levantar infraestructura. |
| Extensibilidad y Evolución | RegisterAvionUseCase, CheckAvionCapacityHandler | Nuevos casos de uso se añaden sin afectar la implementación existente. |
La Arquitectura Hexagonal es más que solo separación de capas. Sus postulados garantizan que la aplicación sea:
✅ Modular y fácil de mantener.
✅ Flexible, permitiendo cambios sin afectar la lógica de negocio.
✅ Escalable, integrando nuevas tecnologías sin modificar el núcleo.
✅ Testeable, permitiendo pruebas sin dependencias externas.
Siguiendo estos principios, tu aplicación en Java con Spring Boot será más robusta, extensible y preparada para el futuro. 🚀
💡 Justificación:
La modularidad garantiza que los cambios en una parte del sistema no afecten otras partes, facilitando la mantenibilidad y la reutilización del código.
📌 Ejemplo aplicado:
🔹 AvionDomainService encapsula la lógica de negocio del avión. Si mañana cambia la forma en que se valida un avión, solo se modifica esta clase sin tocar los controladores ni los adaptadores de infraestructura.
🔹 ¿Qué pasaría si no se tiene en cuenta?
🚨 Si la validación estuviera en los controladores, habría que cambiar múltiples clases (AvionController, AvionRepository), aumentando la probabilidad de errores.
💡 Justificación:
Al depender de abstracciones (interfaces) en lugar de implementaciones concretas, es posible cambiar tecnologías sin modificar el código de negocio.
📌 Ejemplo aplicado:
🔹 AvionRepository es una interfaz del dominio. Hoy puede estar implementada por JpaAvionRepository, pero mañana podría ser MongoAvionRepository sin cambiar el resto del sistema.
🔹 ¿Qué pasaría si no se tiene en cuenta?
🚨 Si AvionDomainService dependiera directamente de JPA (EntityManager o JpaRepository), cambiar a MongoDB implicaría reescribir todo el código de negocio.
💡 Justificación:
Si la aplicación está diseñada siguiendo la arquitectura hexagonal, se pueden agregar nuevas tecnologías sin tocar la lógica central del dominio.
📌 Ejemplo aplicado:
🔹 EventPublisher es una interfaz genérica para publicar eventos. Hoy puede usarse con Kafka (KafkaEventPublisher), pero si se decide cambiar a RabbitMQ, solo se implementa RabbitMQEventPublisher sin afectar AvionEventListener.
🔹 ¿Qué pasaría si no se tiene en cuenta?
🚨 Si AvionDomainService llamara directamente a Kafka, cambiar a otra tecnología requeriría modificar el dominio, lo que rompe la independencia de la infraestructura.
💡 Justificación:
Las pruebas unitarias deben ejecutarse sin requerir bases de datos, APIs externas o frameworks, asegurando rapidez y confiabilidad.
📌 Ejemplo aplicado:
🔹 AvionDomainService se puede probar con un Mock de AvionRepository, sin necesidad de levantar una base de datos real.
🔹 ¿Qué pasaría si no se tiene en cuenta?
🚨 Si AvionDomainService dependiera directamente de JPA o de una API REST, cada prueba requeriría un entorno completo con base de datos y servicios externos, volviendo las pruebas lentas y frágiles.
💡 Justificación:
Los casos de uso están diseñados de forma independiente, lo que permite agregar nuevas funcionalidades sin afectar las ya existentes.
📌 Ejemplo aplicado:
🔹 RegisterAvionUseCase maneja el registro de aviones. Si se requiere agregar una nueva validación (por ejemplo, verificar que la capacidad sea mayor a 10), solo se modifica esta clase sin afectar el resto del sistema.
🔹 ¿Qué pasaría si no se tiene en cuenta?
🚨 Si toda la lógica estuviera dispersa en varias capas, modificar o extender la funcionalidad implicaría tocar múltiples clases (AvionController, AvionRepository, AvionEventListener), aumentando el riesgo de errores.
Aplicando la arquitectura hexagonal, tu aplicación en Java con Spring Boot será:
✅ Modular, con capas bien definidas.
✅ Flexible, permitiendo cambios de tecnología sin afectar el núcleo.
✅ Escalable, integrando nuevas herramientas sin esfuerzo.
✅ Testeable, con pruebas rápidas y confiables.
✅ Robusta, permitiendo agregar nuevas funcionalidades sin romper lo existente.
Siguiendo estos principios, tendrás un sistema más limpio, mantenible y preparado para el futuro. 🚀